(ns packet.example.pip
  (:require [packet.core :as p]
            [packet.utils :as u]
            [packet.bytes :as bs]
            [clojure.string :as str]
            [packet.types :as t]))

(defn cs
  "计算检查和"
  [bs]
  (->> bs
       (map #(bit-and % 0xff))
       (partition 2)
       (map #(let [[h l] %] (+ (* h 256) l)))
       (apply + 0)
       (bit-not)))


(p/defslots options
  ;;仅做示例，IP协议要求选项的长度为4字节的倍数
  (option
   opt-x (t/u1)
   55
   (padding 4))
  (option
   opt-y (t/u2)
   33
   (padding 4)))

(p/defpacket ip
  (bits 1
        ver (0 3 :default 4 "版本号")
        hlen (4 7 :pad 2 "头部长度"))
  tos (t/u "区分服务")
  total-len (length-of data :len 2 "总长度")
  ;;total-len (t/u2 "总长度")
  id (t/u2 "标识符")
  (bits 2 :big-endian
        df (1 "允许分片"
              :enum [:disable-fr :enable-fr]
              :desc {:disable-fr "不允许分片"
                     :enable-fr "允许分片"})
        mf (2 "更多分片")
        frag-offset (3 15 :pad 3 "分片偏移"))
  ttl (t/u "生存时间")
  protocol (enum {1 :ICMP 2 :IGMP 6 :TCP 17 :UDP 88 :IGRP 89 :OSPF} :default :TCP "上层协议")
  (checksum :to dest-ip :len 2 :checksum cs)
  source-ip (t/ipaddr "源地址")
  dest-ip (t/ipaddr "目标地址")
  (slots options :len (- hlen 20))
  data (raw :len total-len "上层报文"))

;;IP协议规定大端在前，与默认的规定相反，因此不要直接用p/parse
;;或p/build来分析或构建报文
(defn parse
  [bs]
  (binding [u/*big-endian* true]
    (p/parse ip bs)))

(defn build
  ([obj] (build obj (transient [])))
  ([obj dst]
   (binding [u/*big-endian* true]
     (p/build ip obj :dst dst))))

